//cMap16Window.cpp
#include "cMap16Window.h"

//Selectable background colours
//The first two are level (i.e. the level's background colour is used) and
//opaque (i.e. colour 0 of the palette is drawn), but the colour is used to fill
//areas where no tile is drawn.
const u32 Map16Window::BGColours[] = {0x000000, 0x000000, 0x000000, 0xFFFFFF,
	0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x808080};

/* Constructor with parent window.
 */
Map16Window::Map16Window(MainWindow *Parent)
{
	this->_Create(Parent);
}

/* PROTECTED: Internal constructor routine.
 */
void Map16Window::_Create(MainWindow *Parent)
{
	GtkWidget *MainBox, *EventBox, *Box;
	char Buf[32];
	
	Window::_Create("16x16 Tiles", 256, 256);
	
	this->Parent = Parent;
	this->ShowGrid = true;
	this->Zoom = 1;
	this->Page = 1;
	this->BGColIdx = 0;
	
	//Connect signals
	gtk_signal_connect(GTK_OBJECT(this->GTKWin), "delete-event",
		(GtkSignalFunc)cMap16Window_DeleteEvent, this);
	
	
	//Main window box
    MainBox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(this->GTKWin), MainBox);
    
    
    //Event box for the drawing area
    EventBox = gtk_event_box_new();
    gtk_box_pack_start(GTK_BOX(MainBox), EventBox, TRUE, TRUE, 0);

    
    //The drawing area itself
    this->TileBuf = new ImgBuf(256, 256);
    this->TileBox = new DrawBox(256, 256);
    
    this->TileBox->InstallConfigureCallback(
    	cMap16Window_TileBoxConfigureEvent, this, true);
    	
    this->TileBox->InstallExposeCallback(
    	cMap16Window_TileBoxExposeEvent, this, false);
    	
    this->TileBox->InstallMouseMoveCallback(cMap16Window_TileBoxMouseMove,
    	this, false);

	gtk_container_add(GTK_CONTAINER(EventBox),
		this->TileBox->GetDrawingArea());
		
	
	//Container for options
	Box = gtk_hbox_new(FALSE, 0);
	
	
	//Zoom selector
	this->ZoomBox = gtk_combo_box_new_text();
	for(u32 i=1; i<5; i++)
	{
		sprintf(Buf, "%ux", i);
		gtk_combo_box_append_text(GTK_COMBO_BOX(this->ZoomBox), Buf);
	}
	gtk_combo_box_set_active(GTK_COMBO_BOX(this->ZoomBox), 0);
	gtk_signal_connect(GTK_OBJECT(this->ZoomBox), "changed",
		(GtkSignalFunc)cMap16Window_ZoomBoxChangeEvent, this);
	gtk_box_pack_start(GTK_BOX(Box), this->ZoomBox, TRUE, TRUE, 0);
	
	
	//Page selector
	this->PageBox = gtk_combo_box_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->PageBox), "All");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->PageBox), "00xx");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->PageBox), "01xx");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->PageBox), "10xx");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->PageBox), "11xx");
	gtk_combo_box_set_active(GTK_COMBO_BOX(this->PageBox), 1);
	gtk_signal_connect(GTK_OBJECT(this->PageBox), "changed",
		(GtkSignalFunc)cMap16Window_PageBoxChangeEvent, this);
	gtk_box_pack_start(GTK_BOX(Box), this->PageBox, TRUE, TRUE, 0);
	
	
	//BG colour selector
	this->BGColBox = gtk_combo_box_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Level");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Colour 0");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Black");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "White");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Red");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Green");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Blue");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Yellow");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Pink");
	gtk_combo_box_append_text(GTK_COMBO_BOX(this->BGColBox), "Gray");
	gtk_combo_box_set_active(GTK_COMBO_BOX(this->BGColBox), 0);
	gtk_signal_connect(GTK_OBJECT(this->BGColBox), "changed",
		(GtkSignalFunc)cMap16Window_BGColBoxChangeEvent, this);
	gtk_box_pack_start(GTK_BOX(Box), this->BGColBox, TRUE, TRUE, 0);
	
	
	//Grid checkbox
	this->GridBox = gtk_check_button_new_with_mnemonic("_Grid");
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(this->GridBox), 1);
	gtk_signal_connect(GTK_OBJECT(this->GridBox), "toggled",
		(GtkSignalFunc)cMap16Window_GridBoxToggleEvent, this);
	gtk_box_pack_start(GTK_BOX(Box), this->GridBox, TRUE, TRUE, 0);
	
	
	//Add the box to the window
	gtk_box_pack_start(GTK_BOX(MainBox), GTK_WIDGET(Box), FALSE, FALSE, 0);
	
	
	//Status label
	this->StatusLabel = GTK_LABEL(gtk_label_new("Ready."));
	gtk_misc_set_alignment(GTK_MISC(this->StatusLabel), 0, 0.5);
	gtk_box_pack_start(GTK_BOX(MainBox), GTK_WIDGET(this->StatusLabel),
		FALSE, FALSE, 0);
	
	gtk_widget_show_all(GTK_WIDGET(this->GTKWin));
}

/* Destructor.
 */
Map16Window::~Map16Window()
{
	if(this->GTKWin) gtk_widget_destroy(GTK_WIDGET(this->GTKWin));
}

/* Redraws the window.
 */
void Map16Window::Redraw()
{
	if(this->TileBox) this->TileBox->InvalidateAll();
}

/* FRIEND: Callback for tile DrawBox configure (resize, create etc) event.
 * Inputs:
 * -Box: The DrawBox.
 * -DrawingArea: The box's drawing area.
 * -Event: The event.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_TileBoxConfigureEvent(DrawBox* Box,
GtkWidget* DrawingArea, GdkEventConfigure* Event, void *Win)
{
	Map16Window *_this = (Map16Window*)Win;
	
	Box->SetFGColour(0, 0, 0);
	Box->DrawRectangle(0, 0, 0, 0, true); //Clear
	if(_this->TileBuf) _this->TileBuf->Resize(Event->width, Event->height);
}

/* FRIEND: Callback for tile DrawBox expose (need repaint) event.
 * Inputs:
 * -Box: The DrawBox.
 * -DrawingArea: The box's drawing area.
 * -Event: The event.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_TileBoxExposeEvent(DrawBox* Box, GtkWidget* DrawingArea,
GdkEventExpose* Event, void *Win)
{
	Map16Window *_this = (Map16Window*)Win;
	u32 Size = _this->Zoom * 16, Width, Height, Start, End, XD, YD, Tile;
	
	if(!_this->Parent) return;
	
	if(_this->BGColIdx > 1)
	{
		_this->TileBuf->SetFGColour(
			(Map16Window::BGColours[_this->BGColIdx] >> 16) & 0xFF,
			(Map16Window::BGColours[_this->BGColIdx] >> 8) & 0xFF,
			Map16Window::BGColours[_this->BGColIdx] & 0xFF);
	}
	else if(!_this->BGColIdx) //Level BG colour
		_this->TileBuf->SetFGColour(RED(_this->Parent->CurrentLevel.BGColour),
		GREEN(_this->Parent->CurrentLevel.BGColour),
		BLUE(_this->Parent->CurrentLevel.BGColour));
	else _this->TileBuf->SetFGColour(0, 0, 0);
	_this->TileBuf->Clear();
	
	//Tiles
	if(!_this->Page)
	{
		Start = 0;
		End = 4;
	}
	else
	{
		Start = _this->Page - 1;
		End = _this->Page;
	}
	
	XD = 0;
	YD = 0;
	for(u32 P = Start; P < End; P++)
	{
		for(u8 Y = 0; Y < 16; Y++)
		{
			for(u8 X = 0; X < 16; X++)
			{
				Tile = ((Y * 16) + X + (256 * P)) * 4;
				for(u32 T=0; T<4; T++)
				{
					_this->Parent->CurrentLevel.GFX.RenderMagnifiedRawTile(
						(P < 2) ? _this->Parent->CurrentLevel.Map16Tile[Tile+T]
						: _this->Parent->CurrentLevel.BGMap16Tile[Tile - 512],
						(u16*)_this->Parent->CurrentLevel.Palette,
						_this->TileBuf,
						(X * 16) + XD + ((T & 2) * 4),
						(Y * 16) + YD + ((T & 1) * 8), _this->BGColIdx != 1,
						_this->Zoom);
				}
			}
		}
		if(!XD) XD = 256;
		else
		{
			XD = 0;
			YD = 256;
		}
	}
	
	//Copy the buffer to the box
	Box->BlitFrom(_this->TileBuf, 0, 0, 0, 0, _this->TileBuf->GetWidth(),
		_this->TileBuf->GetHeight());
	
	//Draw grid
	if(_this->ShowGrid)
	{
		Width = Box->GetWidth();
		Height = Box->GetHeight();
		
		Box->SetFGColour(64, 64, 64);
		for(u32 y=Size; y<Height; y += Size)
			Box->DrawLine(0, y, Width, y);
		for(u32 x=Size; x<Width; x += Size)
			Box->DrawLine(x, 0, x, Height);
	}
}

/* FRIEND: Callback for level DrawBox mouse move event.
 * Inputs:
 * -Box: The DrawBox.
 * -DrawingArea: The box's drawing area.
 * -Event: The event.
 * -X, Y: The mouse cursor position relative to the drawing area.
 * -State: The mouse button state.
 * -Win: Pointer to the MainWindow the box belongs to.
 */
void cMap16Window_TileBoxMouseMove(DrawBox* Box, GtkWidget* DrawingArea,
GdkEventMotion* Event, u32 X, u32 Y, GdkModifierType State, void *Win)
{
	Map16Window *_this = (Map16Window*)Win;
	u32 Tile = 0, Size = _this->Page ? 16 : 32;
	char Buf[64];
	
	if(_this->Page == 2) Tile = 0x100;
	else if(_this->Page == 3) Tile = 0x1000;
	else if(_this->Page == 4) Tile = 0x1100;
	
	X /= 16;
	Y /= 16;
	if((X >= Size) || (Y >= Size))
	{
		gtk_label_set_text(_this->StatusLabel, "No tile.");
		return;
	}
	else if(X >= 16) Tile += (Y * 16) + (X - 16) + 0x100;
	else Tile += (Y * 16) + X;
	
	if(Y >= 16) Tile += 0xF00;
	
	sprintf(Buf, "Tile 0x%X.", Tile);
	gtk_label_set_text(_this->StatusLabel, Buf);
}

/* FRIEND: Callback for zoom dropdown box change event.
 * Inputs:
 * -Box: The dropdown box.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_ZoomBoxChangeEvent(GtkComboBox *Box, gpointer Win)
{
	Map16Window *_this = (Map16Window*)Win;
	u32 Size = _this->Page ? 256 : 512;
	_this->Zoom = gtk_combo_box_get_active(Box) + 1;
	_this->TileBox->Resize(Size * _this->Zoom, Size * _this->Zoom);
}

/* FRIEND: Callback for page dropdown box change event.
 * Inputs:
 * -Box: The dropdown box.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_PageBoxChangeEvent(GtkComboBox *Box, gpointer Win)
{
	u32 Size;
	Map16Window *_this = (Map16Window*)Win;
	
	_this->Page = gtk_combo_box_get_active(Box);
	Size = _this->Page ? 256 : 512;
	_this->TileBox->Resize(Size * _this->Zoom, Size * _this->Zoom);
	_this->TileBox->InvalidateAll();
}

/* FRIEND: Callback for BG colour dropdown box change event.
 * Inputs:
 * -Box: The dropdown box.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_BGColBoxChangeEvent(GtkComboBox *Box, gpointer Win)
{
	Map16Window *_this = (Map16Window*)Win;
	_this->BGColIdx = gtk_combo_box_get_active(Box);
	_this->TileBox->InvalidateAll();
}

/* FRIEND: Callback for grid checkbox toggle event.
 * Inputs:
 * -Box: The checkbox.
 * -Win: Pointer to the Map16Window the box belongs to.
 */
void cMap16Window_GridBoxToggleEvent(GtkToggleButton *Box, gpointer Win)
{
	Map16Window *_this = (Map16Window*)Win;
	_this->ShowGrid = gtk_toggle_button_get_active(Box);
	_this->TileBox->InvalidateAll();
}

/* FRIEND: Callback for delete_event (window was closed).
 * Inputs:
 * -Widget: Widget that received the event (i.e. the GTK Window).
 * -Event: The event.
 * -Win: Pointer to the Map16Window the box belongs to.
 * Returns: TRUE to stop other handlers, FALSE to propagate further.
 */
gboolean cMap16Window_DeleteEvent(GtkWidget *Widget, GdkEvent *Event, gpointer Win)
{
	Map16Window *_this = (Map16Window*)Win;
	if(_this->TileBox) delete(_this->TileBox); _this->TileBox = NULL;
	if(_this->TileBuf) delete(_this->TileBuf); _this->TileBuf = NULL;
	if(_this->GTKWin) gtk_widget_destroy(GTK_WIDGET(_this->GTKWin));
	_this->GTKWin = NULL;
	return TRUE;
}
